﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;

namespace Xwltz.Framework.Framework
{
    public static class XwltzExtensions
    {

        public static IQueryable<TSource> HasWhere<TSource>(this IQueryable<TSource> query, object target,
            Expression<Func<TSource, bool>> whExpression)
        {
            if (target != null)
            {
                query = query.Where(whExpression);
            }
            return query;
        }

        public static IQueryable<TSource> HasWhere<TSource>(this IQueryable<TSource> query, object target,
            Expression<Func<TSource, int, bool>> whExpression)
        {
            if (target != null)
            {
                query = query.Where(whExpression);
            }
            return query;
        }

        public static List<Type> GetInheritedTypes(this Assembly assembly, Type baseType)
        {
            return assembly.GetTypes().Where(x => x.BaseType != null && x.BaseType.GenericEquals(baseType)).ToList();
        }

        public static bool GenericEquals(this Type type, Type toCompare)
        {
            return type.Namespace == toCompare.Namespace && type.Name == toCompare.Name;
        }

        public static bool Equal(this string input, string toCompare, StringComparison comparison = StringComparison.OrdinalIgnoreCase)
        {
            if (input == null)
            {
                return toCompare == null;
            }
            return input.Equals(toCompare, comparison);
        }

        public static Guid? ToGuid(this string str)
        {
            if (string.IsNullOrEmpty(str))
            {
                return null;
            }

            if (Guid.TryParse(str, out var id))
            {
                return id;
            }
            return null;
        }

        public static int? ToInt32(this string str)
        {
            if (int.TryParse(str, out var value))
            {
                return value;
            }
            return null;
        }

        public static bool IsEmail(this string value)
        {
            var reg = new Regex(@"\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*");
            return string.IsNullOrEmpty(value) == false && reg.IsMatch(value);
        }

        public static void MapColumns(this SqlBulkCopy bulk, DataTable table)
        {
            foreach (DataColumn column in table.Columns)
            {
                bulk.ColumnMappings.Add(column.ColumnName, column.ColumnName);
            }
        }

        public static T To<T>(this T? value) where T : struct => To(value, default(T));

        public static T To<T>(this T? value, T defaultValue) where T : struct => value ?? defaultValue;

        public static string GetAllMessages(this Exception exception)
        {
            var ex = exception;
            var sb = new StringBuilder();
            while (ex != null)
            {
                sb.AppendLine(ex.Message);
                ex = ex.InnerException;
            }
            return sb.ToString();
        }

        public static string JoinString(this IEnumerable<string> values)
        {
            return JoinString(values, ",");
        }

        public static string JoinString(this IEnumerable<string> values, string split)
        {
            var result = values.Aggregate(string.Empty, (current, value) => current + (split + value));
            result = result.TrimStart(split.ToCharArray());
            return result;
        }

        public static IEnumerable<T> Each<T>(this IEnumerable<T> source, Action<T> action)
        {
            if (source == null) return null;
            var enumerable = source as T[] ?? source.ToArray();
            foreach (var item in enumerable)
            {
                action(item);
            }
            return enumerable;
        }

        public static List<T> Distinct<T, TKey>(this IEnumerable<T> source, Func<T, TKey> key) where TKey : class
        {
            if (source == null)
            {
                return null;
            }

            var results = new List<T>();

            foreach (var item in source)
            {
                if (results.All(resultItem => key(resultItem) != key(item)))
                {
                    results.Add(item);
                }
            }
            return results;
        }

        public static void AddRange<T>(this ICollection<T> source, IEnumerable<T> items)
        {
            if (items == null)
            {
                return;
            }

            foreach (var item in items)
            {
                source.Add(item);
            }
        }
    }
}